package services

import (
	"encoding/json"
	"github.com/boticord/backend/notifications/app"
	"github.com/boticord/backend/notifications/types"
	"github.com/boticord/backend/notifications/utils"
	"go.uber.org/fx"
	"io"
	"net/http"
)

func sendResponse[T any](w http.ResponseWriter, data types.Response[T]) error {
	w.Header().Set("Content-Type", "application/json")
	b, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = w.Write(b)
	return err
}

type BroadcastService struct {
	emitter *app.Emitter
	auth    *AuthService
}

func (b *BroadcastService) broadcastWs(msg types.Notify) {
	affected := b.getAffected(msg)
	for _, id := range affected {
		connection := b.emitter.GetConnection(id)
		if connection == nil {
			continue
		}
		go app.Emit(connection, "notify", msg)
	}
}

func (b *BroadcastService) broadcastWebhook(msg types.Notify) {
	// todo(kislball): implement
}

func (b *BroadcastService) Broadcast(msg types.Notify) {
	if msg.WebhookSettings.Enabled == true {
		msg.WebhookSettings = nil
		go b.broadcastWebhook(msg)
	}
	msg.WebhookSettings = nil
	go b.broadcastWs(msg)
}

func (b *BroadcastService) getAffected(msg types.Notify) []string {
	connections := b.auth.GetSessions()
	var affected []string
	for id, connection := range connections {
		if utils.Has(connection.Resources, msg.Affected) {
			affected = append(affected, id)
		}
	}

	return affected
}

func (b *BroadcastService) DisconnectAffected(botID string) {
	affected := b.getAffected(types.Notify{
		Affected: botID,
	})
	for _, id := range affected {
		b.emitter.GetConnection(id).Close()
	}
}

func newBroadcastService() fx.Option {
	return fx.Module("BroadcastService",
		fx.Provide(func(e *app.Emitter, a *AuthService, cfg *app.Config) *BroadcastService {
			cast := &BroadcastService{
				emitter: e,
				auth:    a,
			}

			http.HandleFunc("/rpc/disconnect/", func(w http.ResponseWriter, r *http.Request) {
				if r.Header.Get("X-Boticord-Rpc") != cfg.RpcSecret {
					w.WriteHeader(http.StatusUnauthorized)
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeUnauthorized,
						Message: "нахуя ты в рпц долбишься долбаёб, оскорбление чувств верующих чел",
					}))
					return
				}

				if r.Method != "POST" {
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeBadRequest,
						Message: "Invalid method",
					}))
					return
				}

				var data types.ForceDisconnect
				b, _ := io.ReadAll(r.Body)
				err := json.Unmarshal(b, &data)
				if err != nil {
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeBadRequest,
						Message: err.Error(),
					}))
					return
				}

				cast.DisconnectAffected(data.ID)
				v := "cool"
				sendResponse[string](w, types.Ok(v))
			})

			http.HandleFunc("/rpc/publish/", func(w http.ResponseWriter, r *http.Request) {
				if r.Header.Get("X-Boticord-Rpc") != cfg.RpcSecret {
					w.WriteHeader(http.StatusUnauthorized)
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeUnauthorized,
						Message: "нахуя ты в рпц долбишься долбаёб, оскорбление чувств верующих чел",
					}))
					return
				}

				if r.Method != "POST" {
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeBadRequest,
						Message: "Invalid method",
					}))
					return
				}

				var data types.Notify
				b, _ := io.ReadAll(r.Body)
				err := json.Unmarshal(b, &data)
				if err != nil {
					sendResponse[any](w, types.Err[any](types.Error{
						Code:    types.ErrorCodeBadRequest,
						Message: err.Error(),
					}))
					return
				}

				cool := "cool"
				cast.Broadcast(data)
				sendResponse(w, types.Ok(cool))
			})

			return cast
		}),
		fx.Invoke(func(s *BroadcastService) {}),
	)
}
